home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d1 / diff25.arc / DIFF.C next >
C/C++ Source or Header  |  1989-05-06  |  20KB  |  767 lines

  1. /*----------------------------------------------------------------------------
  2.  
  3.     diff.c -- file compare and change bar inserter for text files
  4.     originally produced by D Krantz, modified for Turbo C by P van Es
  5.     date 4/19/88 to rev 2.3.  Rev 2.4 and 2.5 mods by Steve Kraus.
  6.  
  7.     5/2/88    v1.1    options have been UNIX-fied
  8.     5/4/88  v1.2    comparisons made faster
  9.                     bugs corrected
  10.                     upper case handling rationalised
  11.     6/14/88 v2.0    automatic resync feature added
  12.     6/18/88 v2.1    screen listing switchable
  13.     6/20/88 v2.2    resync made faster for small changes
  14.     6/21/88 v2.3    resync checksum lookup added
  15.     01/13/89 v2.4    wrote error messages to STDERR, left help on STDOUT
  16.                     allowed uppercase or lowercase command line options
  17.                     with either the / or the - switch character
  18.     05/05/89 v2.5    added -n to display line numbers instead of page & line
  19.                     numbers for programmers.  -n comes from FIND & FC usage.
  20.                     Renamed previous -n option to -u for update file.
  21.                     added -m n left margin comparison option.  -m 5 starts
  22.                     comparison at column 5 on both files
  23.  
  24.   ---------------------------------------------------------------------------*/
  25.  
  26. /* file difference utility */
  27.  
  28. #if defined (__COMPACT__)
  29. #else
  30. #error Must be compiled with COMPACT model
  31. #endif
  32.  
  33. #include <ctype.h>
  34. #include <stdio.h>
  35.  
  36. typedef unsigned char    byte;    /* define type byte, vals 0-255 */
  37. #define MAXLINE   83    /* maximum characters in the input line */
  38. #define FORMFEED  'L'-'@'
  39. #define EOF_REACHED    0    /* eof during resync */
  40. #define RE_SYNCED    1    /* we managed to re-sync */
  41. #define NO_SYNC        2    /* no sync but no eof either */
  42.  
  43. struct LINE {            /* structure defining a line internally */
  44.     int linenum;        /* what line on page */
  45.     int pagenum;        /* what page */
  46.     byte checksum;        /* use a checksum for quick comparison */
  47.     struct LINE *prev;    /* pointer to previous line */
  48.     struct LINE *link;    /* linked list pointer */
  49.     struct LINE *same;    /* pointer to next line with checksum */
  50.     char text [MAXLINE];    /* text of line */
  51. };
  52.  
  53. typedef struct LINE *line_ptr;
  54. typedef char *char_ptr;
  55. typedef FILE *FILE_PTR;
  56. struct LINE root[3];        /* root of internal linked list */
  57. int line_count[3] = {1,1,1};    /* file's line counter */
  58. int page_count[3] = {1,1,1};    /* file's page counter */
  59. int command_errors = 0;        /* number of command line errors */
  60. char xx1[132], xx2[132];    /* space for file names */
  61. int files = 0;            /* nr of files in command line */
  62. char_ptr infile_name[3] = {NULL,xx1,xx2};
  63. char outfile_name[132];        /* output file name */
  64. FILE_PTR infile[3];        /* input file pointers */
  65. FILE *outfile;            /* changebarred output file ptr */
  66.  
  67. static line_ptr at[3] = {NULL, &(root[1]), &(root[2])};
  68. static line_ptr same_check [256] [2];
  69.  
  70. int debug = 0;            /* trace switch */
  71. int trace_enabled = 0;        /* keyboard tracing switch */
  72. int bar_col = 78;        /* column for change bar */
  73. int top_skip = 0;        /* lines to skip at top of page */
  74. int bot_skip = 0;        /* lines to skip at bottom of page */
  75. int page_len = 66;        /* length of a page */
  76. int up_case = 0;        /* boolean - ignore case */
  77. int re_sync = 3;        /* lines that must match for resync */
  78. int output = 0;            /* boolean - is change barred outfile on */
  79. int blanks = 0;            /* boolean - are blank lines significant */
  80. int skip1 = 0;            /* pages in first file to skip */
  81. int skip2 = 0;            /* pages in second file to skip */
  82. int scr_on = 1;            /* screen listing turned on */
  83. int leftcol = 0;            /* first column to compare - 1 */
  84. int numflag = 0;            /* print line count instead of page# : line# */
  85.  
  86. #if 0 /* tracing and other debug functions turned off */
  87.  
  88. #define trace( x )    callstack( x )
  89. #define ret        { callpop(); return; }
  90. #define ret_val( x )    { callpop(); return( x ); }
  91. #define TRACER_FUNCTIONS
  92.  
  93. #else
  94.  
  95. #define trace( x )    /* nothing */
  96. #define ret        { return; }
  97. #define ret_val( x )    { return( x ); }
  98.  
  99. #endif
  100.  
  101. /*--------------------------------------------------------------------------*/
  102. main (argc, argv)
  103.     int argc;
  104.     char *argv[];
  105. {
  106.     int i;
  107.     trace ( "main" );
  108.     if (argc == 1)
  109.         help();
  110.     else
  111.         fputs ("diff -- version 2.5 05/05/89\n", stderr);
  112.  
  113.     strip_opt (argc, argv);
  114.     if (files<2) {
  115.         fputs ("\nError: Must specify two files", stderr);
  116.         exit (2);
  117.     }
  118.     open_files();
  119.     if (command_errors)
  120.         exit (2);
  121.  
  122.     /* set root previous pointers to 0 */
  123.     at[1]->prev = NULL;
  124.     at[2]->prev = NULL;
  125.  
  126.     page_skip();
  127.     diff();
  128.     ret;
  129. }
  130.  
  131. /* dont_look - tells us whether or not this line should be considered for
  132.    comparison or is a filler (eg header, blank) line */
  133.  
  134. dont_look (line)
  135.     line_ptr line;
  136. {
  137.     int i;
  138.     trace ("dont_look");
  139.     if (line == NULL)    /* EOF reached */
  140.         ret_val(NULL);
  141.     if (line->linenum <= top_skip)
  142.         ret_val(1);
  143.     if (line->linenum > page_len - bot_skip)
  144.         ret_val(1);
  145.     if (strlen((line->text)) < leftcol)  /* short line */
  146.         ret_val(1);
  147.     if (!blanks)
  148.     {
  149.         for (i=leftcol; i<MAXLINE; i++)
  150.             switch (line->text[i])
  151.             {
  152.                 case '\0':
  153.                 case '\n': ret_val(1);
  154.                 case '\t':
  155.                 case ' ':  break;
  156.                 default:   ret_val(0);
  157.             }
  158.            }
  159.     ret_val(0);
  160. }
  161.  
  162. /* equal - tells us if the pointers 'a' and 'b' point to line buffers
  163.    containing equivalent text or not */
  164.  
  165. equal (a,b)
  166.     line_ptr a,b;
  167. {
  168.     char *pa,*pb;            /* pointer to start of text in A and B */
  169.     trace ("equal");
  170.     if ((a==NULL) || (b==NULL))
  171.         ret_val(0);
  172.     if (a->checksum != b->checksum)
  173.         ret_val(0);
  174.     pa=a->text + leftcol;        /* adjust comparison pointers */
  175.     pb=b->text + leftcol;        /* using left margin column */
  176.     if (up_case)
  177.         ret_val (!strcmp (pa, pb))
  178.     else            /* this function ignores case on comparison */
  179.         ret_val (!stricmp (pa, pb))
  180. }
  181.  
  182. /* position - moves the input pointer for file 'f' such that the next line to
  183.    be read will be 'where' */
  184.  
  185. position (f,where)
  186.     int f;
  187.     line_ptr where;
  188. {
  189.     trace ("position");
  190.     at[f] = &root[f];
  191.     if (where == NULL)
  192.         ret;
  193.     while (at[f]->link != where)
  194.         at[f] = at[f]->link;
  195.     ret;
  196. }
  197.  
  198.  
  199. /* checksum - calculates a simple checksum for the line, and stores it in
  200.    the line buffer.  This allows for faster comparisons */
  201.  
  202. checksum (a)
  203.     line_ptr a;
  204. {
  205.     int i;
  206.     a->checksum = 0;
  207.     for (i=leftcol; a->text[i] != NULL; i++) {
  208.         if (up_case)
  209.             a->checksum ^= a->text[i];
  210.         else
  211.             /* ignore case */
  212.             a->checksum ^= toupper (a->text[i]);
  213.     }
  214. }
  215.  
  216.  
  217. /* next_line - allocates, links and returns the next line from file 'f' if
  218.    no lines are buffered, otherwise returns the next buffered line from 'f'
  219.    and updates the link pointer to the next buffered line; it also inserts
  220.    the line in the correct place in the array same_check */
  221.  
  222. line_ptr next_line (f)
  223.     int f;
  224. {
  225.     char *malloc();
  226.     line_ptr place_hold, start;
  227.  
  228.     trace ("next_line");
  229.     if (at[f]->link != NULL) {
  230.         at[f] = at[f]->link;
  231.         ret_val (at[f]);
  232.     }
  233.     else {
  234.         at[f]->link = (line_ptr) malloc (sizeof(struct LINE));
  235.         if (at[f]->link == NULL) {
  236.             fputs ("\nError: Out of Memory", stderr);
  237.             exit (2);
  238.         }
  239.         place_hold = at[f];
  240.         at[f] = at[f]->link;
  241.         if (place_hold == &(root[f]))
  242.             at[f]->prev = NULL;
  243.         else
  244.             at[f]->prev = place_hold;
  245.         at[f]->link = NULL;
  246.         at[f]->same = NULL;
  247.         if (fgets (at[f]->text, MAXLINE, infile[f]) == NULL) {
  248.             free (at[f]);
  249.             at[f] = place_hold;
  250.             at[f]->link = NULL;
  251.             at[f]->same = NULL;
  252.             ret_val (NULL)
  253.         }
  254.         /* calculate a checksum for the new line of text */
  255.         checksum (at[f]);
  256.  
  257. #ifdef EMBEDDED_FORMFEEDS
  258.         if ((strchr (at[f]->text, FORMFEED) != NULL) ||
  259.             (line_count[f] > page_len))
  260. #else
  261.         if ((*(at[f]->text) == FORMFEED) ||
  262.             (line_count[f] > page_len))
  263. #endif
  264.         {
  265.             page_count[f]++;
  266.             line_count[f]=1;
  267.         }
  268.         at[f]->linenum = line_count[f]++;
  269.         at[f]->pagenum = page_count[f];
  270.  
  271.         /* insert it in the correct place in the array unless it is
  272.            a dont_look line */
  273.         if (!dont_look (at[f])) {
  274.              start = same_check [at[f]->checksum][f-1];
  275.             if (start == NULL)
  276.                 same_check [at[f]->checksum][f-1] = at[f];
  277.             else {
  278.                 while (start->same != NULL)
  279.                     start = start->same;
  280.                 /* start is NULL now, insert at[f] here */
  281.                 start->same = at[f];
  282.             }
  283.         }
  284.  
  285.         ret_val (at[f]);
  286.     }
  287. }
  288.  
  289. /* discard - deallocates all buffered lines from the root up to and inclu-
  290.    ding 'to' for file 'f', including from same_check */
  291.  
  292. discard (f,to)
  293.     int f;
  294.     line_ptr to;
  295. {
  296.     line_ptr temp;
  297.     trace ("discard");
  298.     for (;;) {
  299.         if (root[f].link == NULL || to == NULL)
  300.             break;
  301.  
  302.         temp = root[f].link;
  303.         /* ok the line exists, now find the record in same_check */
  304.         if (!dont_look (temp)) {
  305.             /* replace with temp->same */
  306.             same_check [temp->checksum][f-1] = temp->same;
  307.         }
  308.  
  309.         root[f].link = root[f].link->link;
  310.         root[f].link->prev = NULL;
  311.         free (temp);
  312.         if (temp == to)
  313.             break;
  314.     }
  315.     at[f] = &root[f];
  316.     ret;
  317. }
  318.  
  319. /* put - if change barred output file is turned on, prints all lines from
  320.    the root of file 1 up to and including 'line'.  This is only called if
  321.    a match exists for each significant line in file 2 */
  322.  
  323. put (line)
  324.     line_ptr line;
  325. {
  326.     line_ptr temp;
  327.     trace ("put");
  328.     if (output)
  329.         for (temp = root[1].link; ;) {
  330.             if (temp == NULL)
  331.                 ret
  332.             fputs (temp->text, outfile);
  333.             if (temp == line)
  334.                 ret
  335.             temp = temp->link;
  336.         }
  337.     ret;
  338. }
  339.  
  340. /* change_bar - inserts a change bar into the text pointed to by 'str'
  341.    and returns a pointer to 'str' */
  342.  
  343. char *change_bar (str)
  344.     char *str;
  345. {
  346.     int i;
  347.     char temp [MAXLINE+1], *dest, *base;
  348.     trace ("change_bar");
  349.     base = str;
  350.     dest = temp;
  351.     i = 0;
  352.     if (bar_col != 0) {
  353.         for (i=leftcol; *str != '\n'; i++) {
  354.             if ((*str == '\r') && (*(str+1) != '\n'))
  355.                 i = 0;
  356.             *(dest++) = *(str++);
  357.         }
  358.         while (i++ < bar_col)
  359.             *(str)++ = ' ';
  360.         strcpy (str, "|\n");
  361.     }
  362.     else {
  363.         if (str[0] != ' ') {
  364.             strcpy (temp,str);
  365.             strcpy (str+1,temp);
  366.         }
  367.         str[0] = '|';
  368.     }
  369.     ret_val (base);
  370. }
  371.  
  372. /* added - prints a change summary for all significant lines from the root
  373.    of file 1 up to and including 'line'.  If output is enabled, adds a
  374.    change bar to the text and outputs the line to the output file */
  375.  
  376. added (line)
  377.     line_ptr line;
  378. {
  379.     line_ptr temp;
  380.     trace ("added");
  381.     for (temp = root[1].link; ;) {
  382.         if (temp == NULL)
  383.             ret
  384.         if (!dont_look (temp) && scr_on)
  385.             if (numflag) printf ("%.5d< %s",
  386.                 (temp->pagenum-1)*page_len+temp->linenum, temp->text);
  387.             else printf ("%.3d:%.2d< %s",
  388.                 temp->pagenum, temp->linenum, temp->text);
  389.         if (output)
  390.             if (dont_look (temp))
  391.                 fputs (temp->text, outfile);
  392.             else
  393.                 fputs (change_bar (temp->text), outfile);
  394.         if (temp == line)
  395.             ret
  396.         temp = temp->link;
  397.     }
  398. }
  399.  
  400. /* deleted - outputs a change summary for all lines i file 2 from the root
  401.    up to and including 'line' */
  402.  
  403. deleted (line)
  404.     line_ptr line;
  405. {
  406.     line_ptr temp;
  407.     trace ("deleted");
  408.     for (temp = root[2].link; ;) {
  409.         if (temp == NULL)
  410.             ret
  411.         if (!dont_look (temp) && scr_on)
  412.             if (numflag) printf ("%.5d> %s",
  413.                 (temp->pagenum-1)*page_len+temp->linenum, temp->text);
  414.             else printf ("%.3d:%.2d> %s", 
  415.                 temp->pagenum, temp->linenum, temp->text);
  416.         if (temp == line)
  417.             ret
  418.         temp = temp->link;
  419.     }
  420. }
  421.  
  422. /* resync - resynchronizes file 1 and file 2 after a difference is detected
  423.    and outputs changed lines and change summaries via added() and deleted().
  424.    Exits with the file inputs pointing at the next two lines that match,
  425.    unless it is impossible to sync up again, in which case all lines in file 1
  426.    are printed via added().  Deallocates lines printed by this function. */
  427.  
  428. resync (first, second,lookahead)
  429.     line_ptr first, second;
  430.     int lookahead;
  431. {
  432.     line_ptr file1_start, file2_start, last_bad1, last_bad2, t1, t2,
  433.         check;
  434.     int i, j, k, moved1, moved2;
  435.     trace ("resync");
  436.  
  437.     /* first ensure sufficient lines in memory */
  438.     position (2, second);
  439.         last_bad2 = second;
  440.     for (k=0; k<lookahead && last_bad2 != NULL ; k++)
  441.         last_bad2 = next_line(2);
  442.  
  443.     /* now reset file pointers */
  444.     moved1 = 0;
  445.     file1_start = first;
  446.     position (1,first);
  447.  
  448.     for (k=0; k < lookahead; k++) {
  449.         while (dont_look (file1_start = next_line(1)));
  450.         if (file1_start == NULL) goto no_sy;
  451.         moved2 = 0;
  452.  
  453.         /* now see if there is a matching line at all */
  454.         check = same_check [file1_start->checksum][1];
  455.         while (check != NULL) {
  456.             /* look for matching entries */
  457.             while (!equal (check, file1_start)) {
  458.                 check = check->same;
  459.                 if (check == NULL)
  460.                     break;
  461.             }
  462.  
  463.             if (check != NULL) {
  464.                 /* ok we have a hit */
  465.                 t1 = file1_start;
  466.                 file2_start = check;
  467.                 t2 = file2_start;
  468.                 position (1, file1_start);
  469.                 position (2, file2_start);
  470.                 for (i=0; (i<re_sync) && equal (t1,t2); i++) {
  471.                     while (dont_look (t1 = next_line (1)));
  472.                     while (dont_look (t2 = next_line (2)));
  473.                     if ((t1 == NULL) || (t2 == NULL))
  474.                         break;
  475.                 }
  476.                 if (i == re_sync) {
  477.                     moved2++;
  478.                     if ((last_bad2 = file2_start->prev) == NULL)
  479.                         moved2 = 0;
  480.                     goto synced;
  481.                 }
  482.                 /* get next entry */
  483.                 check = check->same;
  484.             } /* if check != NULL */
  485.         } /* end of while check != NULL */
  486.  
  487.         /* else no sync, no matching entries yet, loop the for list */
  488.         last_bad1 = file1_start;
  489.         position (1, file1_start);
  490.         while (dont_look (file1_start = next_line (1)));
  491.         moved1++;
  492.  
  493.     } /* for each line in file 1 lookahead */
  494.         ret_val (NO_SYNC);
  495.  
  496. no_sy:
  497.     position (1,first);
  498.     while ((first = next_line(1)) != NULL) {
  499.         added (first);
  500.         discard (1, first);
  501.     }
  502.     ret_val (EOF_REACHED);
  503.  
  504. synced:
  505.     if (moved1) {
  506.         added (last_bad1);
  507.         discard (1, last_bad1);
  508.     }
  509.     position (1, file1_start);
  510.     if (moved2) {
  511.         deleted (last_bad2);
  512.         discard (2, last_bad2);
  513.     }
  514.     position (2, file2_start);
  515.     if (scr_on)
  516.         printf ("\n");
  517.     ret_val (RE_SYNCED);
  518. }
  519.  
  520. /* diff - differencing executive.  Prints and deallocates all lines up to
  521.    where a difference is detected, at which point resync() is called.  Exits
  522.    on end of file 1 */
  523.  
  524. diff()
  525. {
  526.     int look_lines, result;
  527.     line_ptr first, second;
  528.  
  529.     trace ("diff");
  530.     for (;;) {
  531.         while (dont_look (first = next_line (1)));
  532.         if (first == NULL) {
  533.             put (first);
  534.             ret;
  535.         }
  536.         while (dont_look (second = next_line (2)));
  537.         if (equal (first, second)) {
  538.             put (first);
  539.             discard (1,first);
  540.             discard (2,second);
  541.         }
  542.         else {
  543.             look_lines = 10;   /* start with 10 lines look-ahead */
  544.             result = NO_SYNC;
  545.                         while (result == NO_SYNC) {
  546.                 result = resync (first, second, look_lines);
  547.                 look_lines *= 2;
  548.                 /* when look_lines reaches 80, assume a large
  549.                    difference and set it to 400 */
  550.                 if (look_lines == 80)
  551.                     look_lines = 400;
  552.             }
  553.         }
  554.  
  555.         if (second == NULL)
  556.             ret
  557.     }
  558. }
  559.  
  560. /* page_skip - skips the first 'skip1' pages of file 1, and then the first
  561.    'skip2' pages of file 2.  This is useful to jump over tables of contents */
  562.  
  563. page_skip()
  564. {
  565.     line_ptr first, second;
  566.     trace ("page_skip");
  567.     for (;;) {
  568.         first = next_line (1);
  569.         if ((first == NULL) || (first->pagenum > skip1))
  570.             break;
  571.         put (first);
  572.         discard (1, first);
  573.     }
  574.     if (first != NULL)
  575.         position (1, first);
  576.     for (;;) {
  577.         second = next_line (2);
  578.         if ((second == NULL) || (second->pagenum > skip2))
  579.             break;
  580.         discard (2,second);
  581.     }
  582.     if (second != NULL)
  583.         position (2,second);
  584.     ret;
  585. }
  586.  
  587. /* help - outputs usage information */
  588. help()
  589. {
  590.     printf ("\ndiff - text file differencer and change barrer"
  591.         "\nusage: diff [option{option}] newfile oldfile [barfile]"
  592.         "\n"
  593.         "\noptions:");
  594. #ifdef TRACER_FUNCTIONS
  595.     printf ("\n   -t   trace operation, default off");
  596. #endif
  597.     printf ("\n   -b n change Bar column in barfile, default 78"
  598.         "\n   -h n Header lines to skip at top of page, default 0"
  599.         "\n   -f n Footer lines to skip at bottom of page, default = 0"
  600.         "\n   -p n Page size (embedded form feeds override) default = 66"
  601.         "\n   -c   Case. uppercase/lowercase is significant (default is off)"
  602.         "\n   -r n Resync lines that must match before files are considered synced"
  603.         "\n        after differences are found. default = 3"
  604.         "\n   -m n left Margin to start comparison, default = 0"
  605.         "\n   -n   Number display line number instead of page & line number on output"
  606.         "\n        (default is pages/line numbers)"
  607.         "\n   -w   White blank lines are considered significant (default is not)"
  608.         "\n   -s   Screen listing off (default is on)"
  609.         "\n   -u n Updated NEWFILE pages to skip before compare. default = 0,"
  610.         "\n        also sets -o."
  611.         "\n   -o n OLDFILE pages in skip before compare.  Must come after -u."
  612.         "\n        default = 0");
  613.     exit (0);
  614. }
  615.  
  616. /* open_files - opens the input and the output files */
  617. open_files()
  618. {
  619.     int i;
  620.     trace ("open_files");
  621.     for (i=1; i<3; i++)
  622.         if ((infile[i] = fopen (infile_name[i], "r")) == NULL) {
  623.             printf ("\nError: can't open %s", infile_name[i]);
  624.             command_errors++;
  625.         }
  626.     if (files>2)
  627.         if ((outfile = fopen (outfile_name, "w")) == NULL) {
  628.             printf ("\nError: can't create %s", outfile_name);
  629.             command_errors++;
  630.         }
  631.     ret;
  632. }
  633.  
  634.  
  635. /* strip_opt - processes each command line option */
  636. strip_opt (ac,av)
  637.     int ac;
  638.     char *av[];
  639. {
  640.     int i;
  641.     trace ("strip_opt");
  642.     for (i=1; i<ac; i++) {
  643.     if ((av[i][0] == '-') || (av[i][0] == '/')) {
  644.             switch (tolower(av[i][1])) {
  645.                 case 'b': bar_col = atoi (av[++i]);
  646.                       break;
  647.                 case 'h': top_skip = atoi (av[++i]);
  648.                       break;
  649.                 case 'f': bot_skip = atoi (av[++i]);
  650.                       break;
  651.                 case 'p': page_len = atoi (av[++i]);
  652.                       break;
  653.                 case 'm': leftcol = atoi (av[++i]) - 1;
  654.                       break;
  655.                 case 'n': numflag = 1;
  656.                       break;
  657.                 case 'c': up_case = 1;
  658.                       break;
  659.                 case 'r': re_sync = atoi (av[++i]);
  660.                       break;
  661.                 case 'w': blanks = 1;
  662.                       break;
  663.                 case 's': scr_on = 0;
  664.                       break;
  665.                 case 'u': skip1 = skip2 = atoi (av[++i]);
  666.                       break;
  667.                 case 'o': skip2 = atoi (av[++i]);
  668.                       break;
  669. #ifdef TRACER_FUNCTIONS
  670.                 case 't': trace_enabled = debug = 1;
  671.                       break;
  672. #endif
  673.                 default:  fprintf (stderr,"\nUnrecognized option %s", av[i]);
  674.                       command_errors++;
  675.             } /* switch av[i][1] */
  676.         }
  677.         else {
  678.             switch (files) {
  679.                 case 0:    strcpy (infile_name[1], av[i]);
  680.                     break;
  681.                 case 1: strcpy (infile_name[2], av[i]);
  682.                     break;
  683.                 case 2: strcpy (outfile_name, av[i]);
  684.                     output = 1;
  685.                     break;
  686.                 default:
  687.                     fprintf (stderr, "\nError: too many files at %s",av[i]);
  688.                     command_errors++;
  689.             }
  690.             files++;
  691.         }
  692.     } /* for each command line argument */
  693.     if (!scr_on && !output) {
  694.         fputs ("\nError: no output file or screen listing will be generated.", stderr);
  695.         command_errors++;
  696.     }
  697.     ret;
  698. }
  699.  
  700. #ifdef TRACER_FUNCTIONS
  701.  
  702. char_ptr names[20];
  703. int stack = 0;
  704.  
  705. callstack (str)
  706.     char *str;
  707. {
  708.     int i;
  709.     char c;
  710.  
  711.     names[stack++] = str;
  712.     if (debug) {
  713.         for (i=0; i<stack; i++)
  714.             printf ("   ");
  715.         printf ("Entering %s\n",str);
  716.     }
  717.     if (trace_enabled && kbhit()) {
  718.         switch (getch()) {
  719.             case 't':
  720.             case 'T': debug = !debug;
  721.                   break;
  722.             case 's':
  723.             case 'S': printf ("\n------------");
  724.                   for (i = stack-1; i>=0; i--)
  725.                     printf ("\n%s", names[i]);
  726.                   printf ("\n------------\n");
  727.                   printf ("free: %lu\n", coreleft());
  728.                   break;
  729.             default:  break;
  730.         }
  731.     }
  732. }
  733.  
  734. callpop()
  735. {
  736.     int i;
  737.     if (debug) {
  738.         for (i=0; i<stack; i++)
  739.             printf ("   ");
  740.         printf ("Exiting %s\n", names[stack-1]);
  741.     }
  742.     stack--;
  743. }
  744.  
  745. printentry (lp, title)
  746.     line_ptr lp;
  747.     char *title;
  748. {
  749.         if (debug) {
  750.         if (lp == NULL)
  751.             printf ("at %s: NULL entry\n");
  752.         else
  753.             printf ("at %s:  p %.3d:%.2d check %.3d\n",title,lp->pagenum,
  754.                 lp->linenum, lp->checksum);
  755.             printf ("%s",lp->text);
  756.             if (lp->link != NULL)
  757.                 printf ("next line : %s",lp->link->text);
  758.             if (lp->same != NULL)
  759.                 printf ("same line : %s",lp->same->text);
  760.             if (lp->prev != NULL)
  761.                 printf ("prev line : %s",lp->prev->text);
  762.             printf ("\n");
  763.     }
  764. }
  765.  
  766. #endif
  767.